package org.deri.feather.indexer;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.KeywordAnalyzer;
import org.apache.lucene.analysis.PerFieldAnalyzerWrapper;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.util.Version;
import org.deri.feather.common.ConfigurationManager;
import org.deri.feather.common.IRIElement;
import org.deri.feather.common.LiteralElement;
import org.deri.feather.common.Triple;


public class Indexer {

	private Map<String, Analyzer> analyzers = new HashMap<String,Analyzer>();
	private IndexWriter graphWriter;
	private IndexWriter valueWriter;
	
	private String VALUE_INDEX_DIR;
	private String GRAPH_INDEX_DIR;
	
	private Vector<String> indexedEntities = new Vector<String>();
	
	public Indexer() {
		
	}
		
	public void setUp(){
		
		String index = ConfigurationManager.getInstance().getValue("index");
		GRAPH_INDEX_DIR = index + "/graph";
		VALUE_INDEX_DIR = index + "/value";
		
		analyzers.put("value", new KeywordAnalyzer());
		
		/// Index Setup ///
		try {
			
			IndexWriterConfig graphConfig = new IndexWriterConfig(Version.LUCENE_35, new PerFieldAnalyzerWrapper(new KeywordAnalyzer(), analyzers));
			graphWriter = new IndexWriter(FSDirectory.open(new File(GRAPH_INDEX_DIR)), graphConfig);
			System.out.println("Indexing to directory '" + GRAPH_INDEX_DIR + "'...");
			
			if (ConfigurationManager.getInstance().enableKeywordIndex() || ConfigurationManager.getInstance().enableLiteralIndex()) {
				IndexWriterConfig keywordConfig = new IndexWriterConfig(Version.LUCENE_35, new PerFieldAnalyzerWrapper(new StandardAnalyzer(Version.LUCENE_35), analyzers));
				valueWriter = new IndexWriter(FSDirectory.open(new File(VALUE_INDEX_DIR)), keywordConfig);
				System.out.println("Indexing to directory '" + VALUE_INDEX_DIR + "'...");
			}
			
		} catch (CorruptIndexException e) {
			e.printStackTrace();
		} catch (LockObtainFailedException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}	

	public void index(Triple triple, String namedGraphIRI){
			
		try {
		  
			// Indexing the graph topology
			indexTriple(triple);

			// Index entities
			if (ConfigurationManager.getInstance().enableKeywordIndex())
				indexKeywords(triple);
			
			if (ConfigurationManager.getInstance().enableLiteralIndex())
				indexLiterals(triple);

		} catch (CorruptIndexException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void indexTriple(Triple triple) throws CorruptIndexException, IOException {
		Document graphDoc = new Document();
		
		Field subField = new Field("sub", triple.getSubject().getIri(), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
		graphDoc.add(subField);
		
		Field predField = new Field("pred", triple.getPredicate().getIri(), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
		graphDoc.add(predField);
		
		Field objField;
		if (triple.getObject().isIriElement()) {
			objField = new Field("obj", ((IRIElement) triple.getObject()).getIri(), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
			graphDoc.add(objField);
		} else {
			LiteralElement object = (LiteralElement) triple.getObject();
			
			objField = new Field("obj", object.getValue(), Field.Store.YES, Field.Index.ANALYZED);
			graphDoc.add(objField);
			
			if(!object.getLanguage().equals("")){
				Field languageField = new Field("lang", object.getLanguage(), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
				graphDoc.add(languageField);
			}
			
			if(!object.getType().equals("")) {
				Field typeField = new Field("type", object.getType(), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
				graphDoc.add(typeField);
			}
		}
		
		Field graphField = new Field("graph", triple.getGraph().getIri(), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
		graphDoc.add(graphField);
		
		graphWriter.addDocument(graphDoc);
	}
	
	private void indexKeywords(Triple triple) throws CorruptIndexException, IOException {

		//FIXME Is it better check it into memory or direct using Lucene?
		if(!indexedEntities.contains(triple.getSubject().getIri())){
			Document entityDoc = new Document();
			Field uriField = new Field("uri", triple.getSubject().getIri(), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
			entityDoc.add(uriField);
			Field stringField = new Field("value", triple.getSubject().getReadableName(), Field.Store.YES, Field.Index.ANALYZED);
			entityDoc.add(stringField);
			valueWriter.addDocument(entityDoc);
			indexedEntities.add(triple.getSubject().getIri());
		}
		
		if(triple.getObject().isIriElement()){
			IRIElement object = (IRIElement) triple.getObject();
			if(!indexedEntities.contains(object.getIri())){
				Document entityDoc = new Document();
				Field uriField = new Field("uri", object.getIri(), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
				entityDoc.add(uriField);
				Field stringField = new Field("value", object.getReadableName(), Field.Store.YES, Field.Index.ANALYZED);
				entityDoc.add(stringField);
				valueWriter.addDocument(entityDoc);
				indexedEntities.add(object.getIri());
			}
		}
		
	}
	
	private void indexLiterals(Triple triple) throws CorruptIndexException, IOException {
		if(!triple.getObject().isIriElement()){
			Document entityDoc = new Document();
			Field uriField = new Field("uri", triple.getSubject().getIri(), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
			entityDoc.add(uriField);
			Field stringField = new Field("value", ((LiteralElement) triple.getObject()).getValue(), Field.Store.YES, Field.Index.ANALYZED);
			entityDoc.add(stringField);
			valueWriter.addDocument(entityDoc);
			indexedEntities.add(triple.getSubject().getIri());
		}
	}
	
	public void commit(){
		
		try {
			graphWriter.commit();
			if (ConfigurationManager.getInstance().enableKeywordIndex() || ConfigurationManager.getInstance().enableLiteralIndex()) 
				valueWriter.commit();
		} catch (CorruptIndexException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void close(){
		try {
			graphWriter.close();
			if (ConfigurationManager.getInstance().enableKeywordIndex() || ConfigurationManager.getInstance().enableLiteralIndex()) 
				valueWriter.close();
			indexedEntities.clear();
			System.out.println("Indexing [DONE]");
		} catch (CorruptIndexException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
}
